/*this file is a library file containing the thread functions
 * and structs for echoClient/Server and stackClient/Server
 */

#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include "client_server_lib.h"

/*beej's function: wrapping a send() function to make sure big buffers
 * are sent properly
 */
int sendall(int s, char *buf, int *len)
{
	int total = 0;        // how many bytes we've sent
	int bytesleft = *len; // how many we have left to send
	int n;

	while(total < *len) {
		n = send(s, buf+total, bytesleft, 0);
		if (n == -1) { break; }
		total += n;
		bytesleft -= n;
	}

	*len = total; // return number actually sent here

	return n==-1?-1:0; // return -1 on failure, 0 on success
}

/*the function we pass to a thread on new connections in the echo_client program
 *
 */
void *echo_serve_client(void *args){
	int numbytes = -2, fd;
	char buf[MAX_MSG_SIZE];
	char *ExitFlag;
	struct echo_thread_args *eta;

	eta = (struct echo_thread_args *)args;
	fd = eta->fd;
	ExitFlag = eta->ExitFlag;

	fprintf(stdout, "entered with fd: %d\n", fd);

	while (!(*ExitFlag)){
		numbytes = recv(fd, buf, MAX_MSG_SIZE ,0);
		fprintf(stdout, "DEBUG: %d\n", numbytes);     /*DEBUG*/
		if (numbytes == 0){    //connection closed
			break;
		}

		if (numbytes == -1){
			fprintf(stdout, "error handling client\n");
			break;
		}

		buf[numbytes-2] = '\0';        /******DEBUG: i don't know why yet - maybe telent sends 2 bytes extra???*/
		fprintf(stdout, "buf in thread is == %s\n", buf);				/*DEBUG*/
		if (strcmp(buf, "exit") == 0){
			numbytes = 8;
			if (sendall(fd, "bye bye\n", &numbytes) == -1){
				fprintf(stderr, "ERROR: failed to send the whole buffer\n"
						"sent only %d bytes out of %d, ", numbytes, (int)strlen(buf));
			}
			break;
		}

		if (sendall(fd, buf, &numbytes) == -1){
			fprintf(stderr, "ERROR: failed to send the whole buffer\n"
					"sent only %d bytes out of %d, ", numbytes, (int)strlen(buf));
		}
	}

	close(fd);
	fprintf(stdout, "exiting thread\n");  			/*DEBUG*/
	pthread_exit(NULL);

}


/*pushCommand is a predicate
 * returns true iff the buffer holds "PUSH" at the beginning
 */
int pushCommand(char *buffer){
	char *p = NULL;

	p = buffer;
	while (*p != '\0' && *p != ' ')
		p++;

	if (*p == '\0') return 0; //no space means not a push command

	if (*p == ' '){
		*p = '\0';   //mark the space char
		if (strcmp("PUSH", buffer) == 0){
			*p = ' ';    //remove the mark
			return 1;
		}
		else {
			*p = ' ';    //remove the mark
			return 0;
		}
	}

	return 0;    //if we got here it can't be a PUSH command return 0;
}

/*a function ver similar to puchCmd only it extracts the string
 * passed to push - we didn't want to clutter the while loop in serve_client more
 * than we had to
 */
void extractPushStr(char *dqBuf, char *destBuf){
	char *p = NULL;

	p = dqBuf;
	while (*p != '\0' && *p != ' ')
		p++;

	if (*p == '\0') return;
	else {
		*p = '\0';
		strcpy(destBuf, dqBuf);
		*p = ' ';
	}
	return;
}

/*a struct to pass args to serve_client through threads
 * ExitFlag is a global char flag signaling the therad to stop working and pthread_exit()
 */



/*the thread's function to handle a single client on stackServer prog.*/
void *serve_client(void *args){
	int numbytes = -2, fd;
	char buf[MAX_MSG_SIZE], dqBuffer[MAX_MSG_SIZE];
	struct thread_args *ta;
	char *ExitFlag;
	dequeue *dq;

	ta = (struct thread_args *)args;

	fd = ta->fd;
	ExitFlag = ta->ExitFlag;
	dq = ta->dq;

	while (!(*ExitFlag)){
		numbytes = recv(fd, buf, MAX_MSG_SIZE ,0);
		fprintf(stdout, "DEBUG: %d\n", numbytes);     /*DEBUG*/
		if (numbytes == 0){    //connection closed
			break;
		}

		if (numbytes == -1){
			fprintf(stdout, "error handling client\n");
			break;
		}

		buf[numbytes-2] = '\0';        /******DEBUG: i don't know why yet - maybe telent sends 2 bytes extra???*/
		fprintf(stdout, "buf in thread is == %s\n", buf);				/*DEBUG*/
		if (strcmp(buf, "exit") == 0){
			numbytes = 8;
			if (sendall(fd, "bye bye\n", &numbytes) == -1){
				fprintf(stderr, "ERROR: failed to send the whole buffer\n"
						"sent only %d bytes out of %d, ", numbytes, (int)strlen(buf));
			}
			break;
		}
		else if ((pushCommand(buf)) == 1){
			extractPushStr(dqBuffer, buf);
			if (push(dq, dqBuffer) == 0){
				fprintf(stderr, "ERROR: couldn't push to the dequeue\n");
				continue;
			}
		}
		else if (strcmp(buf, "POP") == 0){
			if (pop(dq) == 0){
				fprintf(stderr, "ERROR: couldn't pop() the dequeue\n");
				continue;
			}
		}
		else if (strcmp(buf, "TOP") == 0){
			if (top(dq, buf, sizeof(buf)) == 0){
				fprintf(stderr, "ERROR: couldn't top() the dequeue\n");
				continue;
			}

			numbytes += 7;
			if (sendall(fd, strcat("OUTPUT: ", buf), &numbytes) == -1){
				fprintf(stderr, "ERROR: failed to send the whole buffer\n"
						"sent only %d bytes out of %d, ", numbytes, (int)(strlen(buf)+7));
			}
		}
		/*TODO:
 		 *  add here the dequeue cmds cases
 		 */

	}

	close(fd);
	pthread_exit(NULL);
}
